package com.guiji.text2video.fastvideo.utils;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.NumberUtil;
import org.apache.logging.log4j.util.Strings;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.*;

/**
 * @Desc:
 * @Author: zhouguangsheng
 * @Date: create by 2020/11/17 17:11
 * @Modify By:
 */
public class WavUtils {
    private static final Logger log = LoggerFactory.getLogger(WavUtils.class);

    public static long calcPlay(String wavPath) {
        if (Strings.isBlank(wavPath)) {
            return 0;
        }
        try {
            File file = new File(wavPath);
            return VoiceProcessUtil.getVoiceRunningTime(file);
        } catch (Exception e) {
            log.error("calc wav play time error", e);
        }
        return 0;
    }

    /**
     * 根据PCM文件构建wav的header字段
     * @param srate  Sample rate - 8000, 16000, etc.
     * @param channel Number of channels - Mono = 1, Stereo = 2, etc..
     * @param format Number of bits per sample (16 here)
     * @throws IOException
     */
    public static byte[] buildWavHeader(int dataLength, int srate, int channel, int format) throws IOException {
        byte[] header = new byte[44];

        long totalDataLen = dataLength + 36;
        long bitrate = srate * channel * format;

        header[0] = 'R';
        header[1] = 'I';
        header[2] = 'F';
        header[3] = 'F';
        header[4] = (byte) (totalDataLen & 0xff);
        header[5] = (byte) ((totalDataLen >> 8) & 0xff);
        header[6] = (byte) ((totalDataLen >> 16) & 0xff);
        header[7] = (byte) ((totalDataLen >> 24) & 0xff);
        header[8] = 'W';
        header[9] = 'A';
        header[10] = 'V';
        header[11] = 'E';
        header[12] = 'f';
        header[13] = 'm';
        header[14] = 't';
        header[15] = ' ';
        header[16] = (byte) format;
        header[17] = 0;
        header[18] = 0;
        header[19] = 0;
        header[20] = 1;
        header[21] = 0;
        header[22] = (byte) channel;
        header[23] = 0;
        header[24] = (byte) (srate & 0xff);
        header[25] = (byte) ((srate >> 8) & 0xff);
        header[26] = (byte) ((srate >> 16) & 0xff);
        header[27] = (byte) ((srate >> 24) & 0xff);
        header[28] = (byte) ((bitrate / 8) & 0xff);
        header[29] = (byte) (((bitrate / 8) >> 8) & 0xff);
        header[30] = (byte) (((bitrate / 8) >> 16) & 0xff);
        header[31] = (byte) (((bitrate / 8) >> 24) & 0xff);
        header[32] = (byte) ((channel * format) / 8);
        header[33] = 0;
        header[34] = 16;
        header[35] = 0;
        header[36] = 'd';
        header[37] = 'a';
        header[38] = 't';
        header[39] = 'a';
        header[40] = (byte) (dataLength  & 0xff);
        header[41] = (byte) ((dataLength >> 8) & 0xff);
        header[42] = (byte) ((dataLength >> 16) & 0xff);
        header[43] = (byte) ((dataLength >> 24) & 0xff);

        return header;
    }


    /**
     * 默认写入的pcm数据是8000采样率，16bit，后期如果有调整，则可以增加参数
     * @param filePath
     * @param pcmData
     */
    public static boolean writeToFile(String filePath, byte[] pcmData){
        BufferedOutputStream bos = null;
        try {
            if (!FileUtil.exist(new File(filePath))){
                FileUtil.touch(new File(filePath));
            }
            bos = new BufferedOutputStream(new FileOutputStream(filePath));
            // byte[] header = buildWavHeader(pcmData.length, 16000, 1, 16);
            // bos.write(header, 0, 44);
            bos.write(pcmData);
            bos.close();
            return true;
        } catch (Exception e) {
            log.warn("写入文件出现异常" + filePath, e);
        }finally {
            if (bos!=null) {
                try {
                    bos.close();
                } catch (IOException e) {
                    log.warn("关闭流时出现异常" + filePath, e);
                }
            }
        }

        return false;
    }


    /*public static void main(String[] args) throws Exception {
        final File[] ls = FileUtil.ls("D:\\data\\aiopen\\text2video\\wavs\\");
        for (File l : ls) {
            Console.log(l.getAbsolutePath());
            Console.log(calculationDuration(l.getAbsolutePath()));
        }
    }*/

    /*
     * 计算指定wav文件的音频时长
     * */
    public static double calculationDuration(String wavPath) {
        byte[] bytes = new byte[44];
        try {
            new FileInputStream(new File(wavPath)).read(bytes);
        } catch (IOException e) {
            e.printStackTrace();
            return 0;
        }
        double div = 0;
        double duration = 0;
        try {
            // 采样率
            byte[] b = new byte[4];
            short[] bs = new short[4];
            ArrayUtil.copy(bytes,24,b,0,4);
            for (int i = 0; i < b.length; i++) {
                bs[i] = getUint8(b[i]);
            }
            int samplingRate = shortArrayToInt(bs);

            // 位深度  采样深度
            byte[] e = new byte[2];
            short[] es = new short[2];
            ArrayUtil.copy(bytes,34,e,0,2);
            for (int i = 0; i < e.length; i++) {
                es[i] = getUint8(e[i]);
            }
            int sampling = twoShortArrayToInt(es);

            // 通道数
            byte[] f = new byte[2];
            short[] fs = new short[2];
            ArrayUtil.copy(bytes,22,f,0,2);
            for (int i = 0; i < f.length; i++) {
                fs[i] = getUint8(f[i]);
            }
            int channel = twoShortArrayToInt(fs);

            // 码率 = 采样率 * 位深度 * 通道数 / 8
            // 比如双通道的44.1K 16位采样的码率为176400
            int bitRate = samplingRate * sampling * channel /8;

            // 如果码率计算的结果小于16000，则默认置为32000
            if (bitRate < 16000){
                bitRate = 32000;
            }


            // 数据部分的长度
            byte[] d = new byte[4];
            short[] ds = new short[4];
            ArrayUtil.copy(bytes,4,d,0,4);
            for (int i = 0; i < d.length; i++) {
                ds[i] = getUint8(d[i]);
            }
            int dataLength = shortArrayToInt(ds);

            div = NumberUtil.div(dataLength, bitRate);
            duration = NumberUtil.round(div, 4).doubleValue();
        } catch (Exception exception) {
            exception.printStackTrace();
            return 0;
        }
        return duration;
    }

    /*
     * 32位整数 short 数组转 int
     * */
    public static int shortArrayToInt(short[] data) {
        return (data[3] * 256 * 256 * 256) + (data[2] * 256 * 256) + (data[1] * 256) + (data[0]);
    }

    /*
    * 16位整数 short 数组转 int
    * */
    public static int twoShortArrayToInt(short[] data) {
        return (data[1] * 256) + data[0];
    }


    public static short getUint8(short s){
        return (short) (s & 0x00ff);
    }
}
